;  This file is part of tornado.
; 
;    tornado is free software: you can redistribute it And/Or modify
;    it under the terms of the GNU General Public License As published by
;    the Free Software Foundation, either version 3 of the License, Or
;    any later version.
; 
;    tornado is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.  See the
;    GNU General Public License For more details.
; 
;    You should have received a copy of the GNU General Public License
;    along With tornado.  If Not, see <http://www.gnu.org/licenses/>. 2
; 

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.i createGameClient(GUID.s, nickName.s)
  *c.sTornadoClient = new(sTornadoClient)
  If *c
    InitializeStructure(*c,sTornadoClient)
    *c\mGUID    = GUID
    *c\mPseudo  = nickName
    
    *c\mLastConnectedSlot     = -1
    *c\mLastDisConnectedSlot  = -1
    *c\mEvent                 = #TORNADO_EVENT_NONE
    
    createLogFile(#False,#True)
    
    If *c\mFirstCall = #False 
      If InitNetwork() 
        ProcedureReturn *c
      EndIf 
      *c\mFirstCall = #True 
      ProcedureReturn *c
    EndIf 
    
  EndIf 
  ProcedureReturn #Null 
EndProcedure


; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.i getGameInformation(*c.sTornadoClient, ip.s, port.w, timeOut.i = 5000)
  *packet.sPacket = createPacket(#PACKET_SYSTEM)
  
  connection.i  = OpenNetworkConnection(ip,port,#PB_Network_UDP)
  deadLineTimer = ElapsedMilliseconds() + timeOut
  
  If connection
    
    writeLog("try to get information from ip "+ip+":"+Str(port))
    
    header.a = #CLIENT_REQUEST_INFO
    pushPacket(*packet,@header,#TYPE_ASCII)
    pushPacket(*packet,@*c\mGUID,#TYPE_STRING)
    
    Repeat
      event.i = NetworkClientEvent(connection)
      
      If event = #PB_NetworkEvent_Data
        
        *buffer = AllocateMemory(#PACKET_LIMIT)
        ReceiveNetworkData(connection,*buffer,#PACKET_LIMIT)
        *rp.sPacket = getPacketFromData(*buffer)
        
        If *rp<>#Null
          packetID.a    = 0
          popPacket(*rp,@packetID)
          
          If packetID = #SERVER_SEND_BAD_GUID
            writeLog("not compatible server ( bad guid )")
            FreeMemory(*buffer)
            freePacket(*rp)
            ProcedureReturn #Null
          EndIf 
          
          If packetID = #SERVER_SEND_INFORMATION
            *si.sServerInformation = new(sServerInformation)
            If *si
              
              *si\mName$    = Space(512)
              *si\mVersion$ = Space(512)
              *si\mIP$      = ip
              *si\mPort     = port
              popPacket(*rp,@*si\mNbPlayer)
              popPacket(*rp,@*si\mMaxPlayer)
              popPacket(*rp,@*si\mProtect)
              popPacket(*rp,@*si\mName$)
              popPacket(*rp,@*si\mVersion$)
              
              
              ReDim *c\mClientSlot(*si\mMaxPlayer)
              
              message.s = "Session name : "+*si\mName$+#ENDOFLINE 
              message + "Players : "+Str(*si\mNbPlayer)+"/"+Str(*si\mMaxPlayer)+#ENDOFLINE
              message + "Game version : "+*si\mVersion$+#ENDOFLINE
              
              writeLog(message)
              
              freePacket(*packet)
              CloseNetworkConnection(connection)
              freePacket(*rp)
              FreeMemory(*buffer)
              ProcedureReturn *si
            EndIf
          EndIf 
          freePacket(*rp)
        EndIf
        
        FreeMemory(*buffer)
      EndIf 
      
      If timerToSend < ElapsedMilliseconds() And event <> #PB_NetworkEvent_Data
        timerToSend = ElapsedMilliseconds() + 100
        SendNetworkData(connection,*packet\mData,#PACKET_LIMIT)
      EndIf
      
    Until deadLineTimer < ElapsedMilliseconds()
    CloseNetworkConnection(connection)
  EndIf 
  writeLog("Can't retrieve information from server.")
  freePacket(*packet)
  ProcedureReturn #Null 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.i joinServerGame(*c.sTornadoClient, *serverInfo.sServerInformation, gameVersion$, password$,timeout.i=5000)
  *c\mClientID = OpenNetworkConnection(*serverInfo\mIP$, *serverInfo\mPort ,#PB_Network_UDP)
  writeLog("Try to join server.")
  If *c\mClientID
    writeLog("Connection successful.")  
    *packet.sPacket = createPacket(#PACKET_SYSTEM)
    
    header.a = #CLIENT_TRY_TO_CONNECT
    pushPacket(*packet,@header,#TYPE_ASCII)
    pushPacket(*packet,@*c\mPseudo,#TYPE_STRING)
    pushPacket(*packet,@password$,#TYPE_STRING)
    pushPacket(*packet,@*c\mGUID,#TYPE_STRING)
    pushPacket(*packet,@gameVersion$,#TYPE_STRING)
    pushPacket(*packet,@*serverInfo\mName$,#TYPE_STRING)
    
    deadLineTimer = ElapsedMilliseconds() + timeOut
    haveSlot.l    = -1
    Repeat
      clientEvent.i = NetworkClientEvent(*c\mClientID)
      
      
      If clientEvent = #PB_NetworkEvent_Data
        *buffer = AllocateMemory(#PACKET_LIMIT)
        ReceiveNetworkData(*c\mClientID,*buffer,#PACKET_LIMIT)
        
        *rp.sPacket = getPacketFromData(*buffer)
        
        If *rp<>#Null 
          
          If getPacketType(*rp) = #PACKET_SYSTEM
            
            packetID.a    = 0
            popPacket(*rp,@packetID)
            
            *c\mLastPingRequest = ElapsedMilliseconds() + 5000
            
            Select packetID
              Case #SERVER_SEND_BAD_GUID
                *c\mJoinError = "Bad guid"
                writeLog("Connection failed : " + *c\mJoinError)
                FreePacket(*rp)
                FreeMemory(*buffer)
                ProcedureReturn #False 
              Case #SERVER_SEND_BAD_VERSION    
                *c\mJoinError = "Bad version"
                writeLog("Connection failed : " + *c\mJoinError)
                FreePacket(*rp)
                FreeMemory(*buffer)
                ProcedureReturn #False 
              Case #SERVER_SEND_BAD_GAMENAME   
                *c\mJoinError = "Bad session name"
                writeLog("Connection failed : " + *c\mJoinError)
                FreePacket(*rp)
                FreeMemory(*buffer)
                ProcedureReturn #False 
              Case #SERVER_SEND_BAD_PASSWORD   
                *c\mJoinError = "Bad password"
                writeLog("Connection failed : " + *c\mJoinError)
                FreePacket(*rp)
                FreeMemory(*buffer)
                ProcedureReturn #False 
              Case #SERVER_SEND_BAN_BY_IP
                *c\mJoinError = "Ban from ip"
                writeLog("Connection failed : " + *c\mJoinError)
                FreePacket(*rp)
                FreeMemory(*buffer)
                ProcedureReturn #False 
              Case #SERVER_SEND_BAN_BY_PSEUDO  
                *c\mJoinError = "Ban for use forbidden pseudo"
                writeLog("Connection failed : " + *c\mJoinError)
                FreePacket(*rp)
                FreeMemory(*buffer)
                ProcedureReturn #False 
              Case #SERVER_SEND_FULL
                *c\mJoinError = "Server full"
                writeLog("Connection aborded : " + *c\mJoinError)
                FreePacket(*rp)
                FreeMemory(*buffer)
                ProcedureReturn #False 
                
              Case #SERVER_SEND_SLOT
                popPacket(*rp, @haveSlot)
                writeLog("Slot number : "+Str(haveSlot)) 
                *c\mSlot = haveSlot
                
                
              Case #SERVER_SEND_UPDATE_LIST    
                
                slot.l = 0
                name.s = Space(512)
                active.b = #False 
                For i = 0 To *serverInfo\mMaxPlayer-1
                  popPacket(*rp,@slot)
                  popPacket(*rp,@name)
                  popPacket(*rp,@active)
                  *c\mClientSlot(slot)\mClientPseudo = name
                  *c\mClientSlot(slot)\mIsActive     = active
                  writeLog("Player("+Str(slot)+") = "+name)
                Next 
                
                ; *c\mClientUpdate = #True 
                
                FreePacket(*rp)
                FreeMemory(*buffer)
                ProcedureReturn #True 
                
            EndSelect
            
          ElseIf getPacketType(*rp) = #PACKET_CUSTOM   
            
            
            
            AddElement(*c\mCustomPacketReceived())
            
            *c\mCustomPacketReceived()\mData = AllocateMemory(#PACKET_LIMIT)
            CopyMemory(*rp\mData, *c\mCustomPacketReceived()\mData, #PACKET_LIMIT)
            *c\mCustomPacketReceived()\mOffset = 2
            *c\mCustomPacketReceived()\mDestination = *c\mClientID
            *c\mCustomPacketReceived()\mFromIP      = ""
            
            freePacket(*rp)
            
          EndIf
        EndIf 
      EndIf ; // receive data
      
        ;
        ; send request to connect
        ;
        If timerToSend < ElapsedMilliseconds() And clientEvent <> #PB_NetworkEvent_Data And haveSlot = -1
          timerToSend = ElapsedMilliseconds() + 500
          SendNetworkData(*c\mClientID,*packet\mData,#PACKET_LIMIT)
        EndIf
        
         
      Until deadLineTimer < ElapsedMilliseconds()
      CloseNetworkConnection(*c\mClientID)
      ProcedureReturn #False 
    EndIf ; // if *c
    
    writeLog("Connection failed.")  
    closeLog()
    ProcedureReturn #False 
EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL getJoinError(*c.sTornadoClient, *string)
    PokeS(*string,*c\mJoinError)
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL gameClientRun(*c.sTornadoClient)
    If *c
      
      clientEvent.l = NetworkClientEvent(*c\mClientID)
      
      ;
      ; receive data
      ;
      If clientEvent = #PB_NetworkEvent_Data
        
        *buffer = AllocateMemory(#PACKET_LIMIT)
        ReceiveNetworkData(*c\mClientID,*buffer,#PACKET_LIMIT)
        
        *rp.sPacket = getPacketFromData(*buffer)
        
        If *rp<>#Null
          If getPacketType(*rp) = #PACKET_SYSTEM
            header.a = 0
            popPacket(*rp,@Header)
            
            Select header
                
              Case #SERVER_SEND_UPDATE_LIST
                
                slot.l = 0
                name.s = Space(512)
                active.b = #False 
                For i = 0 To ArraySize(*c\mClientSlot()) - 1
                  popPacket(*rp,@slot)
                  popPacket(*rp,@name)
                  popPacket(*rp,@active)
                  *c\mClientSlot(slot)\mClientPseudo = name
                  *c\mClientSlot(slot)\mIsActive     = active
                  writeLog("Update player("+Str(slot)+") = "+name)
                Next 
                
                *c\mClientUpdate = #True 
                
              Case #SERVER_SEND_PING
                ;
                ; send pong
                ;
                *packet.sPacket = createPacket(#PACKET_SYSTEM)
                header.a = #CLIENT_SEND_PONG
                pushPacket(*packet,@header,#TYPE_ASCII)
                pushPacket(*packet,@*c\mSlot,#TYPE_LONG)
                SendNetworkData(*c\mClientID,*packet\mData,#PACKET_LIMIT)
                freePacket(*packet)            
                
                *c\mLastPingRequest = ElapsedMilliseconds() + 5000
                
              Case #SERVER_SEND_PING_FOR_SLOT
                
                slot.l
                ping.l
                popPacket(*rp,@slot)
                popPacket(*rp,@ping)
                *c\mClientSlot(slot)\mPing = ping
                
                *c\mEvent     = #TORNADO_EVENT_RECEIVE_PING
                *c\mEventSlot = slot
                
                
              Case #SERVER_SEND_PLAYER_CONNECTED
                slot.l
                popPacket(*rp,@slot)
                *c\mLastConnectedSlot   = slot
                *c\mClientUpdate        = #True 
                
                *c\mEvent     = #TORNADO_EVENT_PLAYER_CONNECTED
                *c\mEventSlot = slot
                
              Case #SERVER_SEND_PLAYER_DISCONNECTED
                slot.l
                popPacket(*rp,@slot)
                *c\mLastDisConnectedSlot   = slot
                *c\mClientUpdate           = #True 
                
                *c\mEvent     = #TORNADO_EVENT_PLAYER_DISCONNECTED
                *c\mEventSlot = slot
                
              Case #SERVER_SEND_PLAYER_CHANGE_NICKNAME
                
                slot.l
                nickname.s = Space(512)
                popPacket(*rp,@slot)
                popPacket(*rp,@nickname)
                
                *c\mClientSlot(slot)\mClientPseudo = nickname
                
                writeLog("Player "+*c\mClientSlot(slot)\mClientPseudo+ " change to "+nickName)            
                
                *c\mEvent     = #TORNADO_EVENT_PLAYER_CHANGE_NICKNAME
                *c\mEventSlot = slot
                
              Case #SERVER_SEND_KICK
                
                reason.s   = Space(512)
                popPacket(*rp,@reason)
                
                writeLog("You are kicked by the server , reason : " + reason)            
                
                *c\mEvent     = #TORNADO_EVENT_KICKED
                *c\mEventSlot = *c\mSlot
                               
                CloseNetworkConnection(*c\mClientID)
                                
                FreeList(*c\mCustomPacketReceived())
                FreeList(*c\mSystemPacketReceived())
                FreeArray(*c\mClientSlot())
                
                writelog("Client shutdown.")
                closeLog()
                       
                *c\mLastConnectedSlot     = -1
                *c\mLastDisConnectedSlot  = -1
                
                
              Case #SERVER_SEND_PLAYER_HAS_BEEN_KICKED
                
                slot.l
                reason.s   = Space(512)
                popPacket(*rp,@slot)
                popPacket(*rp,@reason)
                
                writeLog("Player "+*c\mClientSlot(slot)\mClientPseudo+" has been kicked by server , reason : "+reason)
                
                *c\mEvent     = #TORNADO_EVENT_PLAYER_KICKED
                *c\mEventSlot = slot
                
                
                
            EndSelect
            freePacket(*rp)
          ElseIf  getPacketType(*rp) = #PACKET_CUSTOM
            
            AddElement(*c\mCustomPacketReceived())
            
            *c\mCustomPacketReceived()\mData = AllocateMemory(#PACKET_LIMIT)
            CopyMemory(*rp\mData, *c\mCustomPacketReceived()\mData, #PACKET_LIMIT)
            *c\mCustomPacketReceived()\mOffset = 2
            *c\mCustomPacketReceived()\mDestination = *c\mClientID
            *c\mCustomPacketReceived()\mFromIP      = ""
            
            freePacket(*rp)
          EndIf 
          
          
          
        EndIf 
        
        FreeMemory(*buffer)
        
      EndIf 
      
      
      ;
      ; send system packets
      ;
      If *c\mPacketManager\mSystemTimer < ElapsedMilliseconds()
        *c\mPacketManager\mSystemTimer  = ElapsedMilliseconds() + 100
        ForEach *c\mPacketManager\mSystemPacket()
          SendNetworkData(*c\mClientID,*c\mPacketManager\mSystemPacket()\mData,#PACKET_LIMIT)
          FreeMemory(*c\mPacketManager\mSystemPacket()\mData)
          DeleteElement(*c\mPacketManager\mSystemPacket())
        Next 
      EndIf 
      
      ;
      ; send custom packets
      ;
      If *c\mPacketManager\mCustomTimer < ElapsedMilliseconds()
        *c\mPacketManager\mCustomTimer  = ElapsedMilliseconds() + 30
        ForEach *c\mPacketManager\mCustomPacket()
          SendNetworkData(*c\mClientID,*c\mPacketManager\mCustomPacket()\mData,#PACKET_LIMIT)
          FreeMemory(*c\mPacketManager\mCustomPacket()\mData)
          DeleteElement(*c\mPacketManager\mCustomPacket())
        Next 
      EndIf
      
      ;
      ; send alive signal every 1500 ms
      ;
      If *c\mAliveTimer < ElapsedMilliseconds()
        *c\mAliveTimer  = ElapsedMilliseconds() + 1500
        *packet.sPacket = createPacket(#PACKET_SYSTEM)
        header.a = #CLIENT_SEND_ALIVE_SIGNAL
        pushPacket(*packet,@header,#TYPE_ASCII)
        pushPacket(*packet,@*c\mSlot,#TYPE_LONG)
        clientAddPacket(*packet, *c)
      EndIf 
      
      
      
      ;
      ; loose connection
      ;
      If *c\mLastPingRequest < ElapsedMilliseconds()
        *c\mEvent     = #TORNADO_EVENT_LOOSE_CONNECTION
        *c\mEventSlot = *c\mSlot 
      EndIf 
      
      
    EndIf 
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL requestClientPing(*c.sTornadoClient, slot.l)
    If *c
      *packet.sPacket = createPacket(#PACKET_SYSTEM)
      header.a = #CLIENT_SEND_GIVE_MY_PING
      pushPacket(*packet,@header,#TYPE_ASCII)
      pushPacket(*packet,@slot,#TYPE_LONG)     ; for who ?
      clientAddPacket(*packet, *c)
    EndIf 
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL.l getClientSlot(*c.sTornadoClient)
    If *c
      ProcedureReturn *c\mSlot  
    EndIf 
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL.l getClientPing(*c.sTornadoClient, slot.l)
    If *c
      If *c\mClientSlot(slot)\mIsActive = #True 
        ProcedureReturn *c\mClientSlot(slot)\mPing
      EndIf 
    EndIf 
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL.b clientIsConnected(*c.sTornadoClient)
    If *c
      If *c\mLastPingRequest < ElapsedMilliseconds()
        CloseNetworkConnection(*c\mClientID)
        ProcedureReturn #False 
      Else
        ProcedureReturn #True 
      EndIf 
    EndIf 
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL clientDisconnect(*c.sTornadoClient)
    If *c
      *packet.sPacket = createPacket(#PACKET_SYSTEM)
      header.a = #CLIENT_SEND_DISCONNECT
      pushPacket(*packet,@header,#TYPE_ASCII)
      pushPacket(*packet,@*c\mSlot,#TYPE_LONG)
      SendNetworkData(*c\mClientID,*packet\mData,#PACKET_LIMIT)
      CloseNetworkConnection(*c\mClientID)
      
      
      FreeList(*c\mCustomPacketReceived())
      FreeList(*c\mSystemPacketReceived())
      FreeArray(*c\mClientSlot())
      
      writelog("Client shutdown.")
      closeLog()
      FreeMemory(*c)
      *c = #Null 
    EndIf 
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
;   ProcedureDLL.b clientIsUpdated(*c.sTornadoClient)
;     If *c
;       result.b = *c\mClientUpdate
;       *c\mClientUpdate = #False 
;       ProcedureReturn result
;     EndIf
;   EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL.b clientSlotIsActive(*c.sTornadoClient, slot.l)
    If *c
      ProcedureReturn *c\mClientSlot(slot)\mIsActive
    EndIf
  EndProcedure
  
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
;   ProcedureDLL.l clientGetLastConnected(*c.sTornadoClient)
;     If *c
;       result.l = *c\mLastConnectedSlot
;       *c\mLastConnectedSlot = -1
;       ProcedureReturn result
;     EndIf 
;   EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
;   ProcedureDLL clientGetLastDisConnected(*c.sTornadoClient)
;     If *c
;       result = *c\mLastDisConnectedSlot
;       *c\mLastDisConnectedSlot = -1 
;       ProcedureReturn result
;     EndIf 
;   EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL clientGetClientNickname(*c.sTornadoClient, slot.l , *string)
    If *c
      result$ = *c\mClientSlot(slot)\mClientPseudo
      PokeS(*string,result$)
    EndIf 
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL clientChangeNickName(*c.sTornadoClient, nickName.s)
    If *c
      *c\mPseudo  = nickName
      *packet.sPacket = createPacket(#PACKET_SYSTEM)
      header.a = #CLIENT_SEND_CHANGE_NICKNAME
      pushPacket(*packet,@header,#TYPE_ASCII)
      pushPacket(*packet,@*c\mSlot,#TYPE_LONG)
      pushPacket(*packet,@nickName,#TYPE_STRING)     
      clientAddPacket(*packet, *c)
    EndIf 
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL.l clientEvent(*c.sTornadoClient)
    If *c
      result.l = *c\mEvent
      *c\mEvent = #TORNADO_EVENT_NONE
      ProcedureReturn result
    EndIf
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
  ProcedureDLL.l clientEventSlot(*c.sTornadoClient)
    If *c
      result.l = *c\mEventSlot
      *c\mEventSlot = -1
      ProcedureReturn result
    EndIf
  EndProcedure
  
  ; /////////////////////////////////////////////////////////////////////////////
  ;
  ; /////////////////////////////////////////////////////////////////////////////
;   ProcedureDLL clientShutdown(*c.sTornadoClient)
;     If *c
;       CloseNetworkConnection(*c\mClientID)
;       ;
;       ;TODO delete all packet
;       ;
;     EndIf 
;   EndProcedure
